#include "StdAfx.h"
#include "sphelper.h"
#include "SAPIController.h"


SAPIController::SAPIController(void)
{
	eventCallback = NULL;
	streamReceiver = NULL;
	pVoice = NULL;
}

SAPIController::~SAPIController(void)
{
}

HRESULT SAPIController::Initialize(SAPIEventCallback* eventCallback, StreamReceiver* streamReceiver, unsigned int samplingRate, WCHAR* voiceName)
{
	this->eventCallback = eventCallback;
	this->streamReceiver = streamReceiver;

	cachedWaveFormat.wFormatTag = WAVE_FORMAT_PCM;
	cachedWaveFormat.nChannels = 1;		
	cachedWaveFormat.nSamplesPerSec = samplingRate;
	cachedWaveFormat.wBitsPerSample = 16;
	cachedWaveFormat.nBlockAlign = (cachedWaveFormat.nChannels*cachedWaveFormat.wBitsPerSample) / 8 ;
	cachedWaveFormat.nAvgBytesPerSec = cachedWaveFormat.nSamplesPerSec*cachedWaveFormat.nBlockAlign;
	cachedWaveFormat.cbSize = 0;
	WAVEFORMATEX* pWaveFormat = &cachedWaveFormat;

    HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
    if(!SUCCEEDED(hr))
	{
		return hr;
	}

	//event callback
	eventCallbackImpl.pVoice = pVoice;
	eventCallbackImpl.owner = eventCallback;
	ULONGLONG myEvents = SPFEI_ALL_TTS_EVENTS;
	pVoice->SetInterest(myEvents, myEvents);
	pVoice->SetNotifySink(&eventCallbackImpl);

	//stream callback		
	CComPtr<ISpStream> cpStream;//internal stream for SAPI
	hr = CoCreateInstance(CLSID_SpStream, NULL, CLSCTX_ALL, IID_ISpStream, (void**)&cpStream);
	if(!SUCCEEDED(hr))
	{
		return hr;
	}

	CComPtr<IStream> cpMemStream;
	cpMemStream = &streamReceiverImpl;
	streamReceiverImpl.cachedWaveFormat = pWaveFormat;
	streamReceiverImpl.owner = streamReceiver;
	hr = cpStream->SetBaseStream(cpMemStream, SPDFID_WaveFormatEx, pWaveFormat);
	if(!SUCCEEDED(hr))
	{
		return hr;
	}
	hr = pVoice->SetOutput(cpStream, true);
	if(!SUCCEEDED(hr))
	{
		return hr;
	}

	if(voiceName)
	{
		hr = SetVoiceName(voiceName);
	}
	return hr;
}

HRESULT SAPIController::SetVoiceName(WCHAR* voiceName)
{
	HRESULT hr = S_OK;
	if(voiceName)
	{
		CComPtr<IEnumSpObjectTokens> cpEnum;
		std::wstring nameStr = L"Name=";
		nameStr += voiceName;
		hr = SpEnumTokens(SPCAT_VOICES, nameStr.c_str(), NULL, &cpEnum);
		if(!SUCCEEDED(hr))
		{
			return hr;
		}
		ULONG ulCount = 0;
		hr = cpEnum->GetCount(&ulCount);
		if(!SUCCEEDED(hr))
		{
			return hr;
		}
		if(ulCount!=1)
		{
			return ERROR_AMBIGUOUS_SYSTEM_DEVICE;
		}
		CComPtr<ISpObjectToken> cpVoiceToken;
		hr = cpEnum->Next(1, &cpVoiceToken, NULL);
		if(!SUCCEEDED(hr))
		{
			return hr;
		}
		hr = pVoice->SetVoice(cpVoiceToken);
	}
	else
	{
		//TODO - perhaps set default voice when voiceName==NULL?
	}
	return hr;
}

void SAPIController::Destroy()
{
	if(pVoice)
	{
		pVoice->Release();
		pVoice = NULL;
	}
}

ISpVoice* SAPIController::GetVoice()
{
	return pVoice;
}


HRESULT SAPIController::Speak(WCHAR* message, bool purgeBeforeSpeak=false)
{
	int purgeFlag = 0;
	if(purgeBeforeSpeak) 
	{
		purgeFlag = SPF_PURGEBEFORESPEAK;
		//pVoice-
	}
	HRESULT hr = pVoice->Speak(message, SPF_ASYNC|purgeFlag, NULL);
	return hr;
}